<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 4.2.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">
  <meta name="google-site-verification" content="yF8HXsFuCTrePDFRnSkN2kkzV2ypBjtywzXsfVXOOV8">
  <meta name="baidu-site-verification" content="VxrVAiQAZ9">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"yang0033.gitee.io","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":true,"preload":true},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="Design Guidelines【译】 RACSequence 的一些约定 默认情况延迟发生 默认会阻塞调用者 副作用只发生一次">
<meta property="og:type" content="article">
<meta property="og:title" content="ReactiveCocoa 设计规范">
<meta property="og:url" content="http://yang0033.gitee.io/2015/10/05/ReactiveCocoa%20%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83/index.html">
<meta property="og:site_name" content="SuperYang&#96;s Blog">
<meta property="og:description" content="Design Guidelines【译】 RACSequence 的一些约定 默认情况延迟发生 默认会阻塞调用者 副作用只发生一次">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2015-10-05T13:22:34.000Z">
<meta property="article:modified_time" content="2020-07-09T08:01:15.824Z">
<meta property="article:author" content="SuperYang">
<meta property="article:tag" content="RAC">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="http://yang0033.gitee.io/2015/10/05/ReactiveCocoa%20%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>ReactiveCocoa 设计规范 | SuperYang`s Blog</title>
  
    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-71812998-3"></script>
    <script>
      if (CONFIG.hostname === location.hostname) {
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());
        gtag('config', 'UA-71812998-3');
      }
    </script>


  <script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?8ff3295288514de6f89f314c23ee40ab";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>




  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">SuperYang`s Blog</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">落魄程序员在线炒饭</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
        <li class="menu-item menu-item-message">

    <a href="/message/" rel="section"><i class="fa fa-music fa-fw"></i>留言板 | Music</a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

    </div>
  </div>

</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://yang0033.gitee.io/2015/10/05/ReactiveCocoa%20%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="https://tva2.sinaimg.cn/crop.259.17.290.290.180/c2abdfa9jw8ez7appr3p2j20g40a2t91.jpg?KID=imgbed,tva&Expires=1594186820&ssig=BdxhDdi1Ti">
      <meta itemprop="name" content="SuperYang">
      <meta itemprop="description" content="牛肉炒饭不要香菜，老板收钱">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="SuperYang`s Blog">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          ReactiveCocoa 设计规范
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2015-10-05 21:22:34" itemprop="dateCreated datePublished" datetime="2015-10-05T21:22:34+08:00">2015-10-05</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2020-07-09 16:01:15" itemprop="dateModified" datetime="2020-07-09T16:01:15+08:00">2020-07-09</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
            </span>

          
            <span id="/2015/10/05/ReactiveCocoa%20%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83/" class="post-meta-item leancloud_visitors" data-flag-title="ReactiveCocoa 设计规范" title="阅读次数">
              <span class="post-meta-item-icon">
                <i class="fa fa-eye"></i>
              </span>
              <span class="post-meta-item-text">阅读次数：</span>
              <span class="leancloud-visitors-count"></span>
            </span>
  
  <span class="post-meta-item">
    
      <span class="post-meta-item-icon">
        <i class="far fa-comment"></i>
      </span>
      <span class="post-meta-item-text">Valine：</span>
    
    <a title="valine" href="/2015/10/05/ReactiveCocoa%20%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83/#valine-comments" itemprop="discussionUrl">
      <span class="post-comments-count valine-comment-count" data-xid="/2015/10/05/ReactiveCocoa%20%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83/" itemprop="commentCount"></span>
    </a>
  </span>
  
  <br>
            <span class="post-meta-item" title="本文字数">
              <span class="post-meta-item-icon">
                <i class="far fa-file-word"></i>
              </span>
                <span class="post-meta-item-text">本文字数：</span>
              <span>6.7k</span>
            </span>
            <span class="post-meta-item" title="阅读时长">
              <span class="post-meta-item-icon">
                <i class="far fa-clock"></i>
              </span>
                <span class="post-meta-item-text">阅读时长 &asymp;</span>
              <span>6 分钟</span>
            </span>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <p><a href="https://github.com/ReactiveCocoa/ReactiveObjC/blob/master/Documentation/DesignGuidelines.md#use-descriptive-declarations-for-methods-and-properties-that-return-a-signal" target="_blank" rel="noopener">Design Guidelines</a>【译】</p>
<h4 id="RACSequence-的一些约定"><a href="#RACSequence-的一些约定" class="headerlink" title="RACSequence 的一些约定"></a>RACSequence 的一些约定</h4><ol>
<li>默认情况延迟发生</li>
<li>默认会阻塞调用者</li>
<li>副作用只发生一次<a id="more"></a> 
<h4 id="RACSignal-的约定"><a href="#RACSignal-的约定" class="headerlink" title="RACSignal 的约定"></a>RACSignal 的约定</h4></li>
<li>信号事件有序串行执行，保证不会同时到达两个或多个信号，但是可以运行在不同的scheduler之上。</li>
<li>订阅事件总会在一个 scheduler 上</li>
<li>错误会立即传播（优先级最高）</li>
<li>订阅事件会产生副作用</li>
<li>订阅关系总会在complete或error时自动销毁</li>
<li>销毁会取消正在执行的任务并且清理相关的资源</li>
</ol>
<h4 id="常用练习"><a href="#常用练习" class="headerlink" title="常用练习"></a>常用练习</h4><ul>
<li><p>为返回信号的方法或属性使用描述性声明<br>声明语义有三个维度</p>
<ol>
<li>冷信号（是否在此时被激活）还是热信号（需要订阅后激活）</li>
<li>信号返回值的个数是0个一个或更多？</li>
<li>信号是否有副作用</li>
</ol>
</li>
<li><p>举个栗子</p>
<ul>
<li><strong>没有副作用的热信号：</strong>通常声明为属性来代替方法。暗示订阅之前不需要任何初始化，并且之后附加的任何订阅不会影响当前的订阅语义（没有副作用且每次订阅都是独立的语义）。信号属性通常命名在事件之后（eg: <code>textChangedSignal</code>）</li>
<li><strong>没有副作用的冷信号：</strong>通常使用一个方法返回信号，并且通常使用名词命名（eg:<code>-currentText</code>）。方法声明暗示信号不会被保存（持有），暗示只有在订阅时才会执行任务。如果信号发送多个值，该名词必须是复数的。(-<code>currentModels</code>）</li>
<li><strong>有副作用的冷信号：</strong>通常使用方法返回，并且使用动词命名（eg:<code>-logIn</code>）.动词命名暗示该方法的执行不是幂等得并且使用该方法的调用者需要小心清楚副作用的影响，除非副作用是被需要的或允许范围内的。如果该信号包含一个或多个值，需要用一个名词命名，并且使用对应的单复数形式。</li>
</ul>
</li>
</ul>
<h4 id="缩进流操作一致"><a href="#缩进流操作一致" class="headerlink" title="缩进流操作一致"></a>缩进流操作一致</h4><p> 如果不适当的格式化，纯流式的代码将会变得很密集并且混乱。使用一致的缩进来高亮链式流的开始和结束。</p>
<p>当在单个流上调用方法时，不需要额外的缩进。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">RACStream *result = [stream startWith:@<span class="number">0</span>];</span><br><span class="line"></span><br><span class="line">RACStream *result2 = [stream map:^(<span class="built_in">NSNumber</span> *value) &#123;</span><br><span class="line">    <span class="keyword">return</span> @(value.integerValue + <span class="number">1</span>);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure>
<p>当多次转换相同的流时，请确保所有步骤都对齐。 复杂运算符<code>+ zip：reduce：</code>或<code>+ combineLatest：reduce：</code>可以拆分为多行以提高可读性：</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">RACStream *result = [[[RACStream</span><br><span class="line">    zip:@[ firstStream, secondStream ]</span><br><span class="line">    reduce:^(<span class="built_in">NSNumber</span> *first, <span class="built_in">NSNumber</span> *second) &#123;</span><br><span class="line">        <span class="keyword">return</span> @(first.integerValue + second.integerValue);</span><br><span class="line">    &#125;]</span><br><span class="line">    filter:^ <span class="built_in">BOOL</span> (<span class="built_in">NSNumber</span> *value) &#123;</span><br><span class="line">        <span class="keyword">return</span> value.integerValue &gt;= <span class="number">0</span>;</span><br><span class="line">    &#125;]</span><br><span class="line">    map:^(<span class="built_in">NSNumber</span> *value) &#123;</span><br><span class="line">        <span class="keyword">return</span> @(value.integerValue + <span class="number">1</span>);</span><br><span class="line">    &#125;];</span><br></pre></td></tr></table></figure>
<p>当然，使用block参数嵌套的流需要从block的自然缩进开始。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">[[signal</span><br><span class="line">    then:^&#123;</span><br><span class="line">        @strongify(<span class="keyword">self</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> [[<span class="keyword">self</span></span><br><span class="line">            doSomethingElse]</span><br><span class="line">            catch:^(<span class="built_in">NSError</span> *error) &#123;</span><br><span class="line">                @strongify(<span class="keyword">self</span>);</span><br><span class="line">                [<span class="keyword">self</span> presentError:error];</span><br><span class="line"></span><br><span class="line">                <span class="keyword">return</span> [RACSignal empty];</span><br><span class="line">            &#125;];</span><br><span class="line">    &#125;]</span><br><span class="line">    subscribeCompleted:^&#123;</span><br><span class="line">        <span class="built_in">NSLog</span>(<span class="string">@"All done."</span>);</span><br><span class="line">    &#125;];</span><br></pre></td></tr></table></figure>

<h4 id="单个流中的所有值类型必须保持一致。"><a href="#单个流中的所有值类型必须保持一致。" class="headerlink" title="单个流中的所有值类型必须保持一致。"></a>单个流中的所有值类型必须保持一致。</h4><blockquote>
<p>使用不同类型需要使用复杂的操作流程，且会给使用流的消费者增加额外的负担。</p>
</blockquote>
<h4 id="避免过久的持有流"><a href="#避免过久的持有流" class="headerlink" title="避免过久的持有流"></a>避免过久的持有流</h4><blockquote>
<p>如果持有流的时间超过他本身需要存活的时间时，将会导致流本身持有的或依赖的资源无法释放，潜在的造成内存使用过高。</p>
</blockquote>
<h4 id="只处理需要的信号"><a href="#只处理需要的信号" class="headerlink" title="只处理需要的信号"></a>只处理需要的信号</h4><blockquote>
<p>除了会增加内存使用，不必要的持有流或者 <code>RACSignal</code> 订阅关系会导致 CPU 使用增加, 因为不必要的任务执行所得出的结果永远不会被使用到。一般可以使用<code>-take</code>或<code>-takeUntil</code>可以自动在不必要的时候取消流执行</p>
</blockquote>
<h4 id="在已知的-Scheduler-上传送信号事件"><a href="#在已知的-Scheduler-上传送信号事件" class="headerlink" title="在已知的 Scheduler 上传送信号事件"></a>在已知的 Scheduler 上传送信号事件</h4><blockquote>
<p>流信号返回的值可能会来自很多复杂的场景，很可能是在底层线程中造作得出，所以在给UI元素这是返回的信号值时，一定要保证其赋值造作发成在<code>mainScheduler</code> (主线程)只上。</p>
</blockquote>
<h4 id="尽可能少的切换调度者（scheduler）"><a href="#尽可能少的切换调度者（scheduler）" class="headerlink" title="尽可能少的切换调度者（scheduler）"></a>尽可能少的切换调度者（<code>scheduler</code>）</h4><blockquote>
<p>非必须的情况下，务必让流信号的执行发生在同一 <code>Scheduler</code> 之上。因为切换 <code>Scheduler</code> 会引入不必要的延迟并且会增加CPU资源的消耗。</p>
</blockquote>
<blockquote>
<p>通常 <code>-deliverOn:</code>严格限制只会在信号链的尾部使用，比如在订阅之前，或为属性绑定值时</p>
</blockquote>
<h4 id="明确信号的副作用"><a href="#明确信号的副作用" class="headerlink" title="明确信号的副作用"></a>明确信号的副作用</h4><blockquote>
<p>通常一定要避免副作用的发生，但是有一些副作用是我们所期望的,一般应用在一些hook方法进行调试，例如 <code>-doNext:</code>、<code>-doError:</code>、<code>-doCompleted:</code>等</p>
</blockquote>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">NSMutableArray</span> *nexts = [<span class="built_in">NSMutableArray</span> array];</span><br><span class="line">__block <span class="built_in">NSError</span> *receivedError = <span class="literal">nil</span>;</span><br><span class="line">__block <span class="built_in">BOOL</span> success = <span class="literal">NO</span>;</span><br><span class="line">RACSignal *bookkeepingSignal = [[[valueSignal</span><br><span class="line">    doNext:^(<span class="keyword">id</span> x) &#123;</span><br><span class="line">        [nexts addObject:x];</span><br><span class="line">    &#125;]</span><br><span class="line">    doError:^(<span class="built_in">NSError</span> *error) &#123;</span><br><span class="line">        receivedError = error;</span><br><span class="line">    &#125;]</span><br><span class="line">    doCompleted:^&#123;</span><br><span class="line">        success = <span class="literal">YES</span>;</span><br><span class="line">    &#125;];</span><br><span class="line"></span><br><span class="line">RAC(<span class="keyword">self</span>, value) = bookkeepingSignal;</span><br></pre></td></tr></table></figure>

<h4 id="使用组播分享带有副作用的信号"><a href="#使用组播分享带有副作用的信号" class="headerlink" title="使用组播分享带有副作用的信号"></a>使用组播分享带有副作用的信号</h4><blockquote>
<p>使用组播可以允许单个信号拥有任意数量的订阅关系。</p>
</blockquote>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// This signal starts a new request on each subscription.</span></span><br><span class="line">RACSignal *networkRequest = [RACSignal createSignal:^(<span class="keyword">id</span>&lt;RACSubscriber&gt; subscriber) &#123;</span><br><span class="line">    AFHTTPRequestOperation *operation = [client</span><br><span class="line">        HTTPRequestOperationWithRequest:request</span><br><span class="line">        success:^(AFHTTPRequestOperation *operation, <span class="keyword">id</span> response) &#123;</span><br><span class="line">            [subscriber sendNext:response];</span><br><span class="line">            [subscriber sendCompleted];</span><br><span class="line">        &#125;</span><br><span class="line">        failure:^(AFHTTPRequestOperation *operation, <span class="built_in">NSError</span> *error) &#123;</span><br><span class="line">            [subscriber sendError:error];</span><br><span class="line">        &#125;];</span><br><span class="line"></span><br><span class="line">    [client enqueueHTTPRequestOperation:operation];</span><br><span class="line">    <span class="keyword">return</span> [RACDisposable disposableWithBlock:^&#123;</span><br><span class="line">        [operation cancel];</span><br><span class="line">    &#125;];</span><br><span class="line">&#125;];</span><br><span class="line"></span><br><span class="line"><span class="comment">// Starts a single request, no matter how many subscriptions `connection.signal`</span></span><br><span class="line"><span class="comment">// gets. This is equivalent to the -replay operator, or similar to</span></span><br><span class="line"><span class="comment">// +startEagerlyWithScheduler:block:.</span></span><br><span class="line">RACMulticastConnection *connection = [networkRequest multicast:[RACReplaySubject subject]];</span><br><span class="line">[connection connect];</span><br><span class="line"></span><br><span class="line">[connection.signal subscribeNext:^(<span class="keyword">id</span> response) &#123;</span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"subscriber one: %@"</span>, response);</span><br><span class="line">&#125;];</span><br><span class="line"></span><br><span class="line">[connection.signal subscribeNext:^(<span class="keyword">id</span> response) &#123;</span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"subscriber two: %@"</span>, response);</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure>

<h4 id="通过给信号命名进行调试"><a href="#通过给信号命名进行调试" class="headerlink" title="通过给信号命名进行调试"></a>通过给信号命名进行调试</h4><figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">RACSignal *signal = [[[RACObserve(<span class="keyword">self</span>, username)</span><br><span class="line">    distinctUntilChanged]</span><br><span class="line">    take:<span class="number">3</span>]</span><br><span class="line">    filter:^(<span class="built_in">NSString</span> *newUsername) &#123;</span><br><span class="line">        <span class="keyword">return</span> [newUsername isEqualToString:<span class="string">@"joshaber"</span>];</span><br><span class="line">    &#125;];</span><br><span class="line"></span><br><span class="line"><span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, signal);</span><br><span class="line"><span class="comment">// 打印结果 [[[RACObserve(self, username)] -distinctUntilChanged] -take: 3] -filter:</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>使用 <a href="https://github.com/ReactiveCocoa/ReactiveObjC/blob/master/ReactiveObjC/RACStream.h" target="_blank" rel="noopener">-setNameWithFormat:</a> 对信号自定义信号名。<br>此外，<code>RACSignal</code> 还提供了 <code>-logNext</code>, <code>-logError</code>,<code>-logCompleted</code>, <code>-logAll</code>方法，当信号事件执行时自动打印信号的信息（包括 <code>name</code>），更方便检测到信号的实时状况。</p>
</blockquote>
<h4 id="避免显式的订阅和销毁"><a href="#避免显式的订阅和销毁" class="headerlink" title="避免显式的订阅和销毁"></a>避免显式的订阅和销毁</h4><blockquote>
<p>虽然-subscribeNext：error：completed：和它的变体是处理信号的最基本的方式，但是它们较少的声明性，鼓励使用副作用和潜在地复制内置功能而使代码复杂化。</p>
</blockquote>
<blockquote>
<p>同时，显式的调用 <code>RACDisposable</code> 类会快速导致一堆乱七八糟的资源管理并且清理代码。</p>
</blockquote>
<blockquote>
<ul>
<li>解决方案 ：使用较高级别的模式来替换手动订阅和销毁<ul>
<li>使用 <code>RAC()</code> 或者 <code>RACChannelTo</code> 宏定义可以用来绑定信号或者属性，代替在状态变化时手动更新。</li>
<li>例如 <code>-takeUntil:</code> 等操作符可以用来当参数中的事件发生时自动销毁一段订阅关系（比如 <code>取消按钮</code> 被点击时）。</li>
</ul>
</li>
</ul>
</blockquote>
<blockquote>
<p>通常 ，相比于使用订阅的回调，使用内置的 <code>stream</code> 和 <code>signal</code> 操作符将会让代码变得更简洁并且能产生更少的易错代码。</p>
</blockquote>
<h4 id="尽可能避免使用-subjects"><a href="#尽可能避免使用-subjects" class="headerlink" title="尽可能避免使用 subjects"></a>尽可能避免使用 subjects</h4><blockquote>
<p><code>Subject</code> 是一个用来桥接代码到 signal 世界的强大工具，但是就像 RAC 中的可变变量一样，当发生滥用时，他们会更快的导致程序变得复杂。</p>
</blockquote>
<blockquote>
<p>Subjects 通常会被 ReactiveCocoa 的其他模式所代替：</p>
<ul>
<li>相比于给 <code>Subject</code> 提供初始值，不如考虑使用  <a href="https://github.com/ReactiveCocoa/ReactiveObjC/blob/master/ReactiveObjC/RACSignal.h" target="_blank" rel="noopener">+createSignal:</a> block 产生值代替。</li>
<li>相比于将一些中间值发送给发送给 subject，不如使用 <code>+combineLatest:</code> 或者 <code>+zip:</code>操作符组合多个信号的输出值。</li>
<li>基于一个 Signal 使用 <a href="https://github.com/ReactiveCocoa/ReactiveObjC/blob/master/Documentation/DesignGuidelines.md#share-the-side-effects-of-a-signal-by-multicasting" target="_blank" rel="noopener">multicast</a> (组播)，而不是使用多个 订阅者共享 <code>subjects</code> 的结果。</li>
<li>使用 <a href="https://github.com/ReactiveCocoa/ReactiveObjC/blob/master/ReactiveObjC/RACCommand.h" target="_blank" rel="noopener">command</a> 或者 <a href="https://github.com/ReactiveCocoa/ReactiveObjC/blob/master/ReactiveObjC/NSObject+RACSelectorSignal.h" target="_blank" rel="noopener">-rac_signalForSelector:</a> ，而不是为了简单的控制一个 subject 实现一个事件方法。</li>
</ul>
</blockquote>
<blockquote>
<p>当不得不使用 subject 时，他们必须是信号链中最基本的输入，而不是其中的一个。</p>
</blockquote>
<h3 id="支持自定义操作符"><a href="#支持自定义操作符" class="headerlink" title="支持自定义操作符"></a>支持自定义操作符</h3><h4 id="推荐使用-RACStream-的方法"><a href="#推荐使用-RACStream-的方法" class="headerlink" title="推荐使用 RACStream 的方法"></a>推荐使用 RACStream 的方法</h4><blockquote>
</blockquote>
<h4 id="尽可能使用现有的操作符进行构思"><a href="#尽可能使用现有的操作符进行构思" class="headerlink" title="尽可能使用现有的操作符进行构思"></a>尽可能使用现有的操作符进行构思</h4><blockquote>
</blockquote>
<h3 id="尽量避免引入并发"><a href="#尽量避免引入并发" class="headerlink" title="尽量避免引入并发"></a>尽量避免引入并发</h3><blockquote>
<p>尽量使用 RACScheduler 代替</p>
</blockquote>
<h4 id="在-disposable-中取消任务和清理所有关联资源。"><a href="#在-disposable-中取消任务和清理所有关联资源。" class="headerlink" title="在 disposable 中取消任务和清理所有关联资源。"></a>在 disposable 中取消任务和清理所有关联资源。</h4><blockquote>
<p>当使用 <a href="https://github.com/ReactiveCocoa/ReactiveObjC/blob/master/ReactiveObjC/RACSignal.h" target="_blank" rel="noopener">+createSignal:</a> 创建信号是，通常会返回一个 <a href="https://github.com/ReactiveCocoa/ReactiveObjC/blob/master/ReactiveObjC/RACDisposable.h" target="_blank" rel="noopener">RACDisposable</a> 对象。而这个 disposable 对象应该：</p>
</blockquote>
<ul>
<li>尽可能方便优雅的取消正在进行的由 <code>signal</code> 开启的任务。</li>
<li>立即销毁任何信号的订阅关系，由此来触发他们的取消和清除代码。</li>
<li>释放任何信号开辟的内存和占用的资源。</li>
</ul>
<h4 id="不要在操作符中分块"><a href="#不要在操作符中分块" class="headerlink" title="不要在操作符中分块"></a>不要在操作符中分块</h4><blockquote>
<p>流操作符应该立即返回一个新的流。操作符需要执行的任何操作都应该是执行新流的一部分，而不是运算符调用本身的一部分。</p>
</blockquote>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// WRONG!</span></span><br><span class="line">- (RACSequence *)map:(<span class="keyword">id</span> (^)(<span class="keyword">id</span>))block &#123;</span><br><span class="line">    RACSequence *result = [RACSequence empty];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">id</span> obj <span class="keyword">in</span> <span class="keyword">self</span>) &#123;</span><br><span class="line">        <span class="keyword">id</span> mappedObj = block(obj);</span><br><span class="line">        result = [result concat:[RACSequence <span class="keyword">return</span>:mappedObj]];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Right!</span></span><br><span class="line">- (RACSequence *)map:(<span class="keyword">id</span> (^)(<span class="keyword">id</span>))block &#123;</span><br><span class="line">    <span class="keyword">return</span> [<span class="keyword">self</span> flattenMap:^(<span class="keyword">id</span> obj) &#123;</span><br><span class="line">        <span class="keyword">id</span> mappedObj = block(obj);</span><br><span class="line">        <span class="keyword">return</span> [RACSequence <span class="keyword">return</span>:mappedObj];</span><br><span class="line">    &#125;];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="避免深度递归导致的堆栈溢出"><a href="#避免深度递归导致的堆栈溢出" class="headerlink" title="避免深度递归导致的堆栈溢出"></a>避免深度递归导致的堆栈溢出</h4><blockquote>
<p>任何可能无限递归的操作符都应该使用 <code>-scheduleRecursiveBlock:</code>  <a href="https://github.com/ReactiveCocoa/ReactiveObjC/blob/master/ReactiveObjC/RACScheduler.h" target="_blank" rel="noopener">RACScheduler</a> 方法。这个方法将会把递归转换为迭代来防止堆栈溢出。<br>例如：下面的例子将会错误地实现 <code>-repeat</code> ,应为这将潜在的引起堆栈溢出并且崩溃。</p>
</blockquote>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">- (RACSignal *)repeat &#123;</span><br><span class="line">    <span class="keyword">return</span> [RACSignal createSignal:^(<span class="keyword">id</span>&lt;RACSubscriber&gt; subscriber) &#123;</span><br><span class="line">        RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];</span><br><span class="line"></span><br><span class="line">        __block <span class="keyword">void</span> (^resubscribe)(<span class="keyword">void</span>) = ^&#123;</span><br><span class="line">            RACDisposable *disposable = [<span class="keyword">self</span> subscribeNext:^(<span class="keyword">id</span> x) &#123;</span><br><span class="line">                [subscriber sendNext:x];</span><br><span class="line">            &#125; error:^(<span class="built_in">NSError</span> *error) &#123;</span><br><span class="line">                [subscriber sendError:error];</span><br><span class="line">            &#125; completed:^&#123;</span><br><span class="line">                resubscribe();</span><br><span class="line">            &#125;];</span><br><span class="line"></span><br><span class="line">            [compoundDisposable addDisposable:disposable];</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> compoundDisposable;</span><br><span class="line">    &#125;];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>相比之下，下面的版本将会避免。</p>
</blockquote>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">- (RACSignal *)repeat &#123;</span><br><span class="line">    <span class="keyword">return</span> [RACSignal createSignal:^(<span class="keyword">id</span>&lt;RACSubscriber&gt; subscriber) &#123;</span><br><span class="line">        RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];</span><br><span class="line"></span><br><span class="line">        RACScheduler *scheduler = RACScheduler.currentScheduler ?: [RACScheduler scheduler];</span><br><span class="line">        RACDisposable *disposable = [scheduler scheduleRecursiveBlock:^(<span class="keyword">void</span> (^reschedule)(<span class="keyword">void</span>)) &#123;</span><br><span class="line">            RACDisposable *disposable = [<span class="keyword">self</span> subscribeNext:^(<span class="keyword">id</span> x) &#123;</span><br><span class="line">                [subscriber sendNext:x];</span><br><span class="line">            &#125; error:^(<span class="built_in">NSError</span> *error) &#123;</span><br><span class="line">                [subscriber sendError:error];</span><br><span class="line">            &#125; completed:^&#123;</span><br><span class="line">                reschedule();</span><br><span class="line">            &#125;];</span><br><span class="line"></span><br><span class="line">            [compoundDisposable addDisposable:disposable];</span><br><span class="line">        &#125;];</span><br><span class="line"></span><br><span class="line">        [compoundDisposable addDisposable:disposable];</span><br><span class="line">        <span class="keyword">return</span> compoundDisposable;</span><br><span class="line">    &#125;];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

    </div>

    
    
    
        

  <div class="followme">
    <p>欢迎关注我的其它发布渠道</p>

    <div class="social-list">

        <div class="social-item">
          <a target="_blank" class="social-link" href="https://www.jianshu.com/u/f37a8f0ba6f8">
            <span class="icon">
              <i class="fas fa-book"></i>
            </span>

            <span class="label">简书</span>
          </a>
        </div>
    </div>
  </div>


      <footer class="post-footer">
          <div class="post-tags">
              <a href="/tags/RAC/" rel="tag"># RAC</a>
          </div>

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2015/10/05/%E6%A1%86%E6%9E%B6%E5%8F%8Ablog%E6%95%B4%E7%90%86/" rel="prev" title="框架及blog整理">
      <i class="fa fa-chevron-left"></i> 框架及blog整理
    </a></div>
      <div class="post-nav-item">
    <a href="/2015/10/06/effective_objective-c%E9%87%8D%E7%82%B9%E6%A0%87%E6%B3%A8/" rel="next" title="effective_objective-c重点标注">
      effective_objective-c重点标注 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          
    <div class="comments" id="valine-comments"></div>

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-4"><a class="nav-link" href="#RACSequence-的一些约定"><span class="nav-number">1.</span> <span class="nav-text">RACSequence 的一些约定</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#RACSignal-的约定"><span class="nav-number">2.</span> <span class="nav-text">RACSignal 的约定</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#常用练习"><span class="nav-number">3.</span> <span class="nav-text">常用练习</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#缩进流操作一致"><span class="nav-number">4.</span> <span class="nav-text">缩进流操作一致</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#单个流中的所有值类型必须保持一致。"><span class="nav-number">5.</span> <span class="nav-text">单个流中的所有值类型必须保持一致。</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#避免过久的持有流"><span class="nav-number">6.</span> <span class="nav-text">避免过久的持有流</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#只处理需要的信号"><span class="nav-number">7.</span> <span class="nav-text">只处理需要的信号</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#在已知的-Scheduler-上传送信号事件"><span class="nav-number">8.</span> <span class="nav-text">在已知的 Scheduler 上传送信号事件</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#尽可能少的切换调度者（scheduler）"><span class="nav-number">9.</span> <span class="nav-text">尽可能少的切换调度者（scheduler）</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#明确信号的副作用"><span class="nav-number">10.</span> <span class="nav-text">明确信号的副作用</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#使用组播分享带有副作用的信号"><span class="nav-number">11.</span> <span class="nav-text">使用组播分享带有副作用的信号</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#通过给信号命名进行调试"><span class="nav-number">12.</span> <span class="nav-text">通过给信号命名进行调试</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#避免显式的订阅和销毁"><span class="nav-number">13.</span> <span class="nav-text">避免显式的订阅和销毁</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#尽可能避免使用-subjects"><span class="nav-number">14.</span> <span class="nav-text">尽可能避免使用 subjects</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#支持自定义操作符"><span class="nav-number"></span> <span class="nav-text">支持自定义操作符</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#推荐使用-RACStream-的方法"><span class="nav-number">1.</span> <span class="nav-text">推荐使用 RACStream 的方法</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#尽可能使用现有的操作符进行构思"><span class="nav-number">2.</span> <span class="nav-text">尽可能使用现有的操作符进行构思</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#尽量避免引入并发"><span class="nav-number"></span> <span class="nav-text">尽量避免引入并发</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#在-disposable-中取消任务和清理所有关联资源。"><span class="nav-number">1.</span> <span class="nav-text">在 disposable 中取消任务和清理所有关联资源。</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#不要在操作符中分块"><span class="nav-number">2.</span> <span class="nav-text">不要在操作符中分块</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#避免深度递归导致的堆栈溢出"><span class="nav-number">3.</span> <span class="nav-text">避免深度递归导致的堆栈溢出</span></a></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="SuperYang"
      src="https://tva2.sinaimg.cn/crop.259.17.290.290.180/c2abdfa9jw8ez7appr3p2j20g40a2t91.jpg?KID=imgbed,tva&Expires=1594186820&ssig=BdxhDdi1Ti">
  <p class="site-author-name" itemprop="name">SuperYang</p>
  <div class="site-description" itemprop="description">牛肉炒饭不要香菜，老板收钱</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">39</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">5</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">31</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/yangchao0033" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;yangchao0033" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="mailto:ygrfwyc@gmail.com" title="E-Mail → mailto:ygrfwyc@gmail.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://weibo.com/yangchao0033" title="Weibo → https:&#x2F;&#x2F;weibo.com&#x2F;yangchao0033" rel="noopener" target="_blank"><i class="fab fa-weibo fa-fw"></i>Weibo</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://www.jianshu.com/u/f37a8f0ba6f8" title="简书 → https:&#x2F;&#x2F;www.jianshu.com&#x2F;u&#x2F;f37a8f0ba6f8" rel="noopener" target="_blank"><i class="fas fa-book fa-fw"></i>简书</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://t.me/superYang0033" title="Telegram → https:&#x2F;&#x2F;t.me&#x2F;superYang0033" rel="noopener" target="_blank"><i class="fab fa-telegram fa-fw"></i>Telegram</a>
      </span>
  </div>



      </div>

      
        <div id="music163player">
        <iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=350 height=86 src="//music.163.com/outchain/player?type=2&id=31473269&auto=0&height=66">
        </iframe>
        </div>
    </div>

    

  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">SuperYang</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-area"></i>
    </span>
    <span title="站点总字数">157k</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
    <span title="站点阅读时长">2:23</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Gemini</a> 强力驱动
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  
  <script>
    (function(){
      var canonicalURL, curProtocol;
      //Get the <link> tag
      var x=document.getElementsByTagName("link");
		//Find the last canonical URL
		if(x.length > 0){
			for (i=0;i<x.length;i++){
				if(x[i].rel.toLowerCase() == 'canonical' && x[i].href){
					canonicalURL=x[i].href;
				}
			}
		}
    //Get protocol
	    if (!canonicalURL){
	    	curProtocol = window.location.protocol.split(':')[0];
	    }
	    else{
	    	curProtocol = canonicalURL.split(':')[0];
	    }
      //Get current URL if the canonical URL does not exist
	    if (!canonicalURL) canonicalURL = window.location.href;
	    //Assign script content. Replace current URL with the canonical URL
      !function(){var e=/([http|https]:\/\/[a-zA-Z0-9\_\.]+\.baidu\.com)/gi,r=canonicalURL,t=document.referrer;if(!e.test(r)){var n=(String(curProtocol).toLowerCase() === 'https')?"https://sp0.baidu.com/9_Q4simg2RQJ8t7jm9iCKT-xh_/s.gif":"//api.share.baidu.com/s.gif";t?(n+="?r="+encodeURIComponent(document.referrer),r&&(n+="&l="+r)):r&&(n+="?l="+r);var i=new Image;i.src=n}}(window);})();
  </script>




  
<script src="/js/local-search.js"></script>













  

  

  

  <script async src="/js/cursor/fireworks.js"></script>



<script>
NexT.utils.loadComments(document.querySelector('#valine-comments'), () => {
  NexT.utils.getScript('//unpkg.com/valine/dist/Valine.min.js', () => {
    var GUEST = ['nick', 'mail', 'link'];
    var guest = 'nick,mail,link';
    guest = guest.split(',').filter(item => {
      return GUEST.includes(item);
    });
    new Valine({
      el         : '#valine-comments',
      verify     : true,
      notify     : true,
      appId      : 'zizRqalUJY55Xc5oBJKbhxpV-gzGzoHsz',
      appKey     : '64gPcVi8lIA8zVGpBSEk7uKu',
      placeholder: "留言板无需注册登录，快来评论吧。。",
      avatar     : 'mm',
      meta       : guest,
      pageSize   : '10' || 10,
      visitor    : true,
      lang       : '' || 'zh-cn',
      path       : location.pathname,
      recordIP   : false,
      serverURLs : ''
    });
  }, window.Valine);
});
</script>

</body>
</html>
